/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012-2013 Oliver Katz */

#include "vector.h"
#include "../debug.h"

z_data z_vector_new()
{
    _z_vector tmp;
    tmp.content = NULL;
    tmp.head = tmp.content;
    tmp.size = 0;
    return z_data_wrap(&tmp, Z_TYPEID_VECTOR, sizeof(_z_vector));
}

z_data *_z_vector_new()
{
    z_data *tmp = (z_data *)malloc(sizeof(z_data));
    *tmp = z_vector_new();
    return tmp;
}

int z_vector_del(z_data *v)
{
    Z_WARNING_DATA(v, 1);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, 2);

    if (tmp->content != NULL)
        free(tmp->content);
    z_data_release(*v);
    return 0;
}

int z_vector_push(z_data *v, z_data e)
{
    Z_WARNING_DATA(v, 1);
    Z_WARNING_DATA(&e, 2);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    //Z_WARNING_VECTOR(tmp, 3);

    if (tmp->content == NULL)
    {
        tmp->size = Z_VECTOR_SIZE_INIT;
        tmp->content = (z_data *)malloc(tmp->size*sizeof(z_data));
        memset(tmp->content, 0, tmp->size*sizeof(z_data));
        tmp->head = tmp->content;

    }
    else if (tmp->head-tmp->content >= tmp->size)
    {
        z_data *buf = (z_data *)malloc(tmp->size*2*sizeof(z_data));
        memset(buf, 0, tmp->size*2*sizeof(z_data));
        memcpy(buf, tmp->content, tmp->size*sizeof(z_data));
        free(tmp->content);
        tmp->head += buf-tmp->content;
        tmp->content = buf;
        tmp->size *= 2;
    }

    *(tmp->head++) = e;
    return 0;
}

int _z_vector_push(z_data *v, z_data *e)
{
    Z_WARNING_DATA(v, 2);
    return z_vector_push(v, *e);
}

z_data z_vector_peek(z_data *v)
{
    Z_WARNING_DATA(v, z_data_init(0, 0));
    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, z_data_init(0, 0));
    return *(tmp->head-1);
}

z_data *_z_vector_peek(z_data *v)
{
    Z_WARNING_DATA(v, NULL);
    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, NULL);
    return tmp->head-1;
}

z_data z_vector_pop(z_data *v)
{
    Z_WARNING_DATA(v, z_data_init(0, 0));
    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, z_data_init(0, 0));
    return *(tmp->head--);
}

z_data *_z_vector_pop(z_data *v)
{
    Z_WARNING_DATA(v, NULL);
    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, NULL);
    return tmp->head--;
}

z_data *z_vector_at(z_data *v, int idx)
{
    Z_WARNING_DATA(v, NULL);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, NULL);

    if (idx >= tmp->head-tmp->content)
    {
        Z_WARNING("vector overflow (%i >= %i)", idx, tmp->head-tmp->content);
        return NULL;
    }
    else
        return tmp->content+idx;
}

int z_vector_size(z_data *v)
{
    Z_WARNING_DATA(v, 1);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, 2);

    return tmp->head-tmp->content;
}

int z_vector_erase(z_data *v, int idx)
{
    Z_WARNING_DATA(v, 1);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, 2);

    if (idx >= tmp->head-tmp->content)
    {
        Z_WARNING("vector overflow (%i >= %i)", idx, tmp->head-tmp->content);
        return 2;
    }
    memcpy(tmp->content+idx, tmp->content+(idx+1), (tmp->head-tmp->content-idx-1)*sizeof(z_data));
    tmp->head--;
    return 0;
}

int z_vector_insert(z_data *vdst, z_data *vsrc, int idx)
{
    Z_WARNING_DATA(vdst, 1);
    Z_WARNING_DATA(vsrc, 2);

    _z_vector *dst = (_z_vector *)z_data_src_get(vdst);
    Z_WARNING_VECTOR(dst, 3);

    _z_vector *src = (_z_vector *)z_data_src_get(vsrc);
    Z_WARNING_VECTOR(src, 4);

    if (idx < 0)
    {
        Z_WARNING("negative indexes don't work (%i)", idx);
        return 5;
    }

    if (idx >= dst->head-dst->content)
    {
        Z_WARNING("vector overflow (%i >= %i)", idx, dst->head-dst->content);
        return 6;
    }

    _z_vector rtn;
    rtn.size = ((int)(dst->head-dst->content))+((int)(src->head-src->content))+1;
    rtn.content = (z_data *)malloc(rtn.size*sizeof(z_data));
    memset(rtn.content, 0, rtn.size*sizeof(z_data));
    memcpy(rtn.content, dst->content, idx*sizeof(z_data));
    memcpy(rtn.content+idx, src->content, (src->head-src->content)*sizeof(z_data));
    memcpy(rtn.content+idx+(src->head-src->content), dst->content+idx, (dst->head-dst->content-idx)*sizeof(z_data));
    z_vector_del(vdst);
    *vdst = z_data_wrap(&rtn, Z_TYPEID_VECTOR, sizeof(_z_vector));
    return 0;
}

char **z_vector_to_array(z_data *v)
{
    Z_WARNING_DATA(v, NULL);

    _z_vector *tmp = (_z_vector *)z_data_src_get(v);
    Z_WARNING_VECTOR(tmp, NULL);

    char **rtn = (char **)malloc(sizeof(char *)*(tmp->head-tmp->content+1));
    memset(rtn, 0, sizeof(char *)*(tmp->head-tmp->content+1));
    for (int i = 0; i < tmp->head-tmp->content; i++)
        rtn[i] = z_string_safe(tmp->content+i);
    return rtn;
}

z_data *z_array_to_vector(char **a)
{
    Z_WARNING_NULL(a, NULL);
    
    z_data *v = _z_vector_new();
    for (int i = 0; a[i] != NULL; i++)
    {
        z_vector_push(v, z_string_new_from(a[i]));
    }
    return v;
}
